<?php
/**
 * Miscelaneous Function API
 *
 * @package OWL
 * @subpackage Core
 * @author KeiMOon and various people
 * @since 1.5
 */

class Owl_Functions {

/**
 * An safety way to include a file
 *
 * @param string $file
 * @return bool
 */
    static function safe_include($file) {
        if (file_exists($file)) {
            include ($file);
            return;
        }
        else {
            $file_exist = false;
            $include_path_array = Owl_Format::add_trailing_slash_deep(explode(PATH_SEPARATOR, get_include_path()));
            foreach($include_path_array as $include_path) {
                if (!file_exists($include_path.$file)) {
                    continue;
                }
                else {
                    $file_exist = true;
                    break;
                }
            }
            if ($file_exist) {
                include ($file);
                return;
            }
            else {
                die("The file $file does not exist in the include paths");
            }
        }
    }

    /**
     * An safety way to include once a file
     *
     * @param string $file
     * @return bool
     */
    static function safe_include_once($file) {
        if (file_exists($file)) {
            include_once ($file);
            return;
        }
        else {
            $file_exist = false;
            $include_path_array = Owl_Format::add_trailing_slash_deep(explode(PATH_SEPARATOR, get_include_path()));
            foreach($include_path_array as $include_path) {
                if (!file_exists($include_path.$file)) {
                    continue;
                }
                else {
                    $file_exist = true;
                    break;
                }
            }
            if ($file_exist) {
                include_once ($file);
                return;
            }
            else {
                die("The file $file does not exist in the include paths");
            }
        }
    }

    static function safe_write_file($file, $data) {
        $handle = @fopen($file, "w");
        if (!$handle) {
            return false;
        }

        if (flock($handle, LOCK_EX)) { // do an exclusive lock
            fwrite($handle, $data);
            flock($handle, LOCK_UN); // release the lock
        }
        else {
            return false;
        }

        fclose($handle);
        return true;
    }

    /**
     * Check if OWL is installed or not
     */
    static function is_installed() {
        return true;
    }

    /**
     * Only allow people from inside
     */
    static function check_referrer() {
        $referer =  Owl_Http::get_http_referrer();
        if (strpos($referer, Owl_Link::site_url(false)) === false) {
            self::go_to_template("login");
        }
    }

    /**
     * A nice way to die
     *
     * @param string $message
     */
    static function hngk_die($message) {
        ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title><?php echo "Sorry!"; ?></title>
        <link type="text/css" rel="stylesheet" href="<?php Owl_Link::style_url(); ?>default.die.css" />
    </head>
    <body>
        <div id="die-content">
            <?php echo $message; ?>
        </div>
    </body>
</html>
        <?php
        die();
    }

    
    /**
     * Convert number of bytes largest unit bytes will fit into.
     *
     * It is easier to read 1kB than 1024 bytes and 1MB than 1048576 bytes. Converts
     * number of bytes to human readable number by taking the number of that unit
     * that the bytes will go into it. Supports TB value.
     *
     * Please note that integers in PHP are limited to 32 bits, unless they are on
     * 64 bit architecture, then they have 64 bit size. If you need to place the
     * larger size then what PHP integer type will hold, then use a string. It will
     * be converted to a double, which should always have 64 bit length.
     *
     * Technically the correct unit names for powers of 1024 are KiB, MiB etc.
     * @link http://en.wikipedia.org/wiki/Byte
     *
     * @param int|string $bytes Number of bytes. Note max integer size for integers.
     * @param int $decimals Precision of number of decimal places.
     * @return bool|string False on failure. Number string on success.
     */
    static function size_format( $bytes, $decimal = 3 ) {
        $quant = array(
            // ========================= Origin ====
            'TB' => 1099511627776,  // pow( 1024, 4)
            'GB' => 1073741824,     // pow( 1024, 3)
            'MB' => 1048576,        // pow( 1024, 2)
            'kB' => 1024,           // pow( 1024, 1)
            'B ' => 1,              // pow( 1024, 0)
        );

        foreach ( $quant as $unit => $mag )
            if ( doubleval($bytes) >= $mag )
                return number_format( $bytes / $mag, $decimal ) . ' ' . $unit;

        return false;
    }

    /**
     * Retrieve a modified URL query string.
     *
     * You can rebuild the URL and append a new query variable to the URL query by
     * using this function. You can also retrieve the full URL with query data.
     *
     * Adding a single key & value or an associative array. Setting a key value to
     * emptystring removes the key. Omitting oldquery_or_uri uses the $_SERVER
     * value.
     *
     * @param mixed $param1 Either newkey or an associative_array
     * @param mixed $param2 Either newvalue or oldquery or uri
     * @param mixed $param3 Optional. Old query or uri
     * @return string New URL query string.
     */
    static function add_query_arg() {
        $ret = '';
        if ( is_array( func_get_arg(0) ) ) {
            if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) )
                $uri = $_SERVER['REQUEST_URI'];
            else
                $uri = @func_get_arg( 1 );
        } else {
            if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) )
                $uri = $_SERVER['REQUEST_URI'];
            else
                $uri = @func_get_arg( 2 );
        }

        if ( $frag = strstr( $uri, '#' ) )
            $uri = substr( $uri, 0, -strlen( $frag ) );
        else
            $frag = '';

        if ( preg_match( '|^https?://|i', $uri, $matches ) ) {
            $protocol = $matches[0];
            $uri = substr( $uri, strlen( $protocol ) );
        } else {
            $protocol = '';
        }

        if ( strpos( $uri, '?' ) !== false ) {
            $parts = explode( '?', $uri, 2 );
            if ( 1 == count( $parts ) ) {
                $base = '?';
                $query = $parts[0];
            } else {
                $base = $parts[0] . '?';
                $query = $parts[1];
            }
        } elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {
            $base = $uri . '?';
            $query = '';
        } else {
            $base = '';
            $query = $uri;
        }

        Owl_Format::parse_str( $query, $qs );
        $qs = Owl_Format::urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
        if ( is_array( func_get_arg( 0 ) ) ) {
            $kayvees = func_get_arg( 0 );
            $qs = array_merge( $qs, $kayvees );
        } else {
            $qs[func_get_arg( 0 )] = func_get_arg( 1 );
        }

        foreach ( (array) $qs as $k => $v ) {
            if ( $v === false )
                unset( $qs[$k] );
        }

        $ret = http_build_query( $qs );
        $ret = trim( $ret, '?' );
        $ret = preg_replace( '#=(&|$)#', '$1', $ret );
        $ret = $protocol . $base . $ret . $frag;
        $ret = rtrim( $ret, '?' );
        return $ret;
    }

    /**
     * Removes an item or list from the query string.
     *
     * @param string|array $key Query key or keys to remove.
     * @param bool $query When false uses the $_SERVER value.
     * @return string New URL query string.
     */
    static function remove_query_arg( $key, $query = false ) {
        if ( is_array( $key ) ) { // removing multiple keys
            foreach ( $key as $k )
                $query = self::add_query_arg( $k, false, $query );
            return $query;
        }
        return self::add_query_arg( $key, false, $query );
    }

    /**
     * Print the css namespace based on browser
     *
     * @param string $classes
     * @return string
     */
    static function print_css_namespace($classes) {
        $str = "";
        if (!Owl_Http::is_robot()) {
            if (Owl_Http::is_Firefox()) {
                $str .= "firefox";
            }
            elseif(Owl_Http::is_Safari()) {
                $str .= "safari";
            }
            elseif (Owl_Http::is_Opera()) {
                $str .= "opera";
            }
            elseif (Owl_Http::is_IE()) {
                $str .= "ie";
            }
            elseif (Owl_Http::is_IE6()) {
                $str .= "ie-6";
            }
            elseif (Owl_Http::is_mobile()) {
                $str .= "mobile";
            }
            if (Owl_HTTP::is_IE6()) {
                $str .= " no-effect";
            }
            else {
                if (get_option("site_effect")) {
                    $str .= " effect";
                }
                else {
                    $str .= " no-effect";
                }
            }
            if ($classes)
                $str .= " $classes";
        }
        echo $str;
    }

    /**
     * Get the relative directory that application is installed in
     *
     * @return string
     */
    static function get_working_dir() {
        return preg_replace("/^http:\\/\\/[\\w\\.\\-]*/i", "", SITE_URL);
    }

    /**
     * Get the current template
     *
     * @return string
     */
    static function current_template() {
        return (empty($_GET["template"]) || $_GET["template"] == "home") ? get_option('home_template', 'note') : $_GET["template"];
    }

    /**
     * Redirect to a certain template, with or without additional arguments
     *
     * @param string $template
     * @param Array $with_args An associative array of additional arguments, or 
     * false if you do not want to pass any. 
     */
    static function go_to_template($template, $with_args = false) {
        $uri = Owl_Link::template_url($template, false);
        if (is_array($with_args) && !empty($with_args)) {
            $uri = self::add_query_arg($with_args, $uri);
        }
        Owl_Http::url_redirect(apply_rewrite_rules($uri));
    }

    /**
     * Build validate string based on metadata and validation plugin of jQuery
     *
     * @param array $validate How to validate the textbox. This array should contain
     * following value: required, minlength:#, maxlength:#, rangelength:#:#,
     * min:#, max:#, range:#:#, email, url, number, digits, equalTo:#, phone
     * @return string Validation string
     */
    static function build_validate_string($validate) {
        $rule_string = '';
        $message_string = 'messages:{';

        $message_list = array (
            "required" => __("This field is required"),
            "minlength" => __("This field requires at least {1} characters"),
            "maxlength" => __("This field requires at most {1} characters"),
            "rangelength" => __("This field requires between {1} and {2} characters"),
            "min" => __("The number must larger than{1}"),
            "max" => __("The number must be smaller than {1}"),
            "range" => __("The number must be in range {1} and {2}"),
            "email" => __("This field requires an email"),
            "url" => __("This field requires an url"),
            "number" => __("This field requires an number"),
            "phone" => __("This field requires a phone number"),
            "digits" => __("Thid field requires all digits"),
            "equalTo" => __("This field must be equal to {1}")
        );

        foreach ($validate as $rule) {
            if (strpos($rule, "required") !== false) {
                $rule_string .= 'required:true, ';
                $message_string .= 'required:' . "'" . $message_list["required"] . "', ";
            }
            elseif (strpos($rule, "minlength:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'minlength:'.$rule[1].', ';
                $message_string .= 'minlength:' . "'" . preg_replace("/\\{1\\}/",$rule[1],$message_list["minlength"]) . "', ";
            }
            elseif (strpos($rule, "maxlength:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'maxlength:'.$rule[1].', ';
                $message_string .= 'maxlength:' . "'" . preg_replace("/\\{1\\}/",$rule[1],$message_list["maxlength"]) . "', ";
            }
            elseif (strpos($rule, "rangelength:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'rangelength:['.$rule[1].', '.$rule[2].'], ';
                $message_string .= 'rangelength:' . "'" . preg_replace(array("/\\{1\\}/","/\\{2\\}/"),array($rule[1], $rule[2]),$message_list["rangelength"]) . "', ";
            }
            elseif (strpos($rule, "min:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'min:'.$rule[1].', ';
                $message_string .= 'min:' . "'" . preg_replace("/\\{1\\}/", $rule[1], $message_list["min"]) . "', ";
            }
            elseif (strpos($rule, "max:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'max:'.$rule[1].', ';
                $message_string .= 'max:' . "'" . preg_replace("/\\{1\\}/", $rule[1], $message_list["max"]) . "', ";
            }
            elseif (strpos($rule, "range:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'range:['.$rule[1].', '.$rule[2].'], ';
                $message_string .= 'range:' . "'" . preg_replace(array("/\\{1\\}/","/\\{2\\}/"),array($rule[1], $rule[2]),$message_list["range"]) . "', ";
            }
            elseif (strpos($rule, "email") !== false) {
                $rule_string .= 'email:true, ';
                $message_string .= 'email:' . "'" . $message_list["email"] . "', ";
            }
            elseif (strpos($rule, "url") !== false) {
                $rule_string .= 'url:true, ';
                $message_string .= 'url:' . "'" . $message_list["url"] . "', ";
            }
            elseif (strpos($rule, "number") !== false) {
                $rule_string .= 'number:true, ';
                $message_string .= 'number:' . "'" . $message_list["number"] . "', ";
            }
            elseif (strpos($rule, "phone") !== false) {
                $rule_string .= 'phone:true, ';
                $message_string .= 'phone:' . "'" . $message_list["phone"] . "', ";
            }
            elseif (strpos($rule, "digits") !== false) {
                $rule_string .= 'digits:true, ';
                $message_string .= 'digits:' . "'" . $message_list["digits"] . "', ";
            }
            elseif (strpos($rule, "equalTo:") !== false) {
                $rule = explode(":", $rule);
                $rule_string .= 'equalTo:\''.$rule[1].'\', ';
                $message_string .= 'equalTo:' . "'" . preg_replace("/\\{1\\}/", $rule[1], $message_list["equalTo"]) . "', ";
            }
        }

        $message_string = preg_replace('/,+(\s)+$/', "", $message_string);

        if (!empty ($rule_string)) {
            return '{'.$rule_string.$message_string.'}}';
        }
    }

    /**
     * A nice way to use print_r function
     *
     * @param mixed $value 
     */
    static function print_r_pre($value) {
        echo "<pre>\n";
        if ($value !== false && $value !== null) {
            print_r($value);
        }
        else {
            var_dump($value);
        }
        echo "</pre>\n";
    }

    /**
     * Encode json value, will attempt to use native PHP function if the JSON extension is
     * availble, or PEAR JSON library if not.
     *
     * @param mix $value
     * @return string
     */
    static function json_encode($value) {
        if (function_exists("json_encode")) {
            return json_encode($value);
        }
        else {
            $json = new Services_JSON();
            return $json->encode($value);
        }
    }

    /**
     * Check if a file is a legitimate directory or not. A legitimate directory
     * shall not have "." in the begining of the file name.
     *
     * @param string $file
     * @return bool
     */
    static function is_legitimate_dir($file) {
        return (!preg_match('/^\./', basename($file)) && is_dir($file));
    }

    /**
     * CHeck if an uploaded file is an image or not
     *
     * @param Array $file_data from $_FILES
     * @param Array $allowed_extension
     * @return bool
     */
    static function file_upload_is_image($file_data, $allowed_extension = array()) {
        $default  = array("jpg", "jpeg", "gif", "png");
        $allowed_extension = Owl_System_Helper::array_merge($allowed_extension, $default);
        $file_info = pathinfo($file_data["name"]);
        return (in_array(strtolower($file_info["extension"]), $allowed_extension));
    }

    /**
     * Get begining and ending index while paging
     *
     * @param int $page
     * @param int $total
     * @return Array|bool
     */
    static function get_pagination_boundary($page, $total) {
        $content_per_page = get_option("content_per_page", 15);
        $begin = ($page - 1) * $content_per_page;
        $end = $begin + $content_per_page - 1;

        if ($begin < $total) {
            if ($end > ($total - 1)) $end = $total - 1;
            return array($begin, $end);
        }
        else {
            return false;
        }
    }

    /**
     * Get page json control text
     *
     * @return Array
     */
    static function get_control_text() {
        return array(
            "detail" => __("Detail"),
            "noData" => __("No data"),
            "prev" => __("Prev"),
            "next" => __("Next"),
            "first" => __("First"),
            "last" => __("Last"),
            "closeForm" => __("Close"),

        );
    }

    /**
     * Print nice file upload error code
     *
     * @param int $error_code
     * @return string 
     */
    static function get_upload_error($error_code) {
        switch($error_code) {
            case 1:
            case 2: return sprintf(__("You cannot upload file larger than %s"), ini_get('upload_max_filesize'));
            case 3: return __("The uploaded file was only partially uploaded.");
            case 4: return __("No file was uploaded.");
            default: return __("Unknown error");
        }
    }

    /**
     * Check if a string is empty or not, this does exclude html tag and white space
     *
     * @param string $content
     * @return bool
     */
    static function content_is_empty($content) {
        $content = strip_tags($content);
        $content = preg_replace('/\s/', '', $content);
        return empty($content);
    }

    /**
     * Get available language
     */
    static function get_available_languages() {
        $langs = array("en" =>"English");
        $handle = @opendir(LANGUAGE_PATH);
        if ($handle === false) {
            return false;
        }
        while (false !== ($file = readdir($handle))) {
            if (strpos($file, ".conf")) {
                list($language_name, $ext) = explode(".", $file);
                if (file_exists(LANGUAGE_PATH . $language_name . ".lang")) {
                    
                    $content = @file_get_contents(LANGUAGE_PATH . $file);
                    $lang_info = explode("::", $content);
                    $langs[$lang_info[0]] = $lang_info[1];
                }
            }
        }
        closedir($handle);
        return $langs;
    }

    /**
     * Get GMT Offset List
     */
    static function get_gmt_offset_list() {
        return array (
            "-12"=>"(GMT -12:00) Eniwetok, Kwajalein",
            "-11"=>"(GMT -11:00) Midway Island, Samoa",
            "-10"=>"(GMT -10:00) Hawaii",
            "-9"=>"(GMT -9:00) Alaska",
            "-8"=>"(GMT -8:00) Pacific Time (US &amp; Canada)",
            "-7"=>"(GMT -7:00) Mountain Time (US &amp; Canada)",
            "-6"=>"(GMT -6:00) Central Time (US &amp; Canada), Mexico City",
            "-5"=>"(GMT -5:00) Eastern Time (US &amp; Canada), Bogota, Lima",
            "-4"=>"(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz",
            "-3"=>"(GMT -3:00) Brazil, Buenos Aires, Georgetown",
            "-2"=>"(GMT -2:00) Mid-Atlantic",
            "-1"=>"(GMT -1:00) Azores, Cape Verde Islands",
            "0"=>"(GMT) Western Europe Time, London, Lisbon, Casablanca",
            "1"=>"(GMT +1:00) Brussels, Copenhagen, Madrid, Paris",
            "2"=>"(GMT +2:00) Kaliningrad, South Africa",
            "3"=>"(GMT +3:00) Baghdad, Riyadh, Moscow, St. Petersburg",
            "4"=>"(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi",
            "5"=>"(GMT +5:00) Ekaterinburg, Islamabad, Karachi, Tashkent",
            "6"=>"(GMT +6:00) Almaty, Dhaka, Colombo",
            "7"=>"(GMT +7:00) Bangkok, Hanoi, Jakarta",
            "8"=>"(GMT +8:00) Beijing, Perth, Singapore, Hong Kong",
            "9"=>"(GMT +9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk",
            "10"=>"(GMT +10:00) Eastern Australia, Guam, Vladivostok",
            "11"=>"(GMT +11:00) Magadan, Solomon Islands, New Caledonia",
            "12"=>"(GMT +12:00) Auckland, Wellington, Fiji, Kamchatka"
        );
    }

    /**
     * Get favorite menu
     *
     * @return bool|array
     */
    static function get_favorite_menu() {
        if ( ($favourite_menu = get_option('favorite_menu')) == false ) {
            return false;
        }
        else {
            $count = array();
            foreach ($favourite_menu as $menu) {
                $count[] = $menu['count'];
            }
            array_multisort($count, SORT_ASC, $favourite_menu);
            return array_slice($favourite_menu, 0, 5);
        }
    }

    static function update_favourite_menu($menu) {
        if ( ($favourite_menu = get_option('favorite_menu')) == false ) {
            $favourite_menu = array(
                'add-page' => array(
                    'name' => 'Add page',
                    'link' => 'add-page',
                    'count' => 0
                ),
                'pages-manager' => array(
                    'name' => 'Pages',
                    'link' => 'pages-manager',
                    'count' => 0
                ),
                'documents-manager' => array(
                    'name' => 'Documents',
                    'link' => 'documents-manager',
                    'count' => 0
                ),
                'system-options' => array(
                    'name' => 'System',
                    'link' => 'system-options',
                    'count' => 0
                ),
                'themes-manager' => array(
                    'name' => 'Themes',
                    'link' => 'themes-manager',
                    'count' => 0
                ),
                'widgets-manager' => array(
                    'name' => 'Widgets',
                    'link' => 'widgets-manager',
                    'count' => 0
                ),
                'password' => array(
                    'name' => 'Password',
                    'link' => 'password',
                    'count' => 0
                )
            );
        }
    }

}

